using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text;

using PerlinNoise.NoiseGenerator;
using PerlinNoise.Interpolator;
using PerlinNoise.Smoother;

namespace PerlinNoise
{
    public class SummedPerlinNoise : SimplePerlinNoise
    {
        private readonly uint octavesNumber;
        private readonly double persistence;

        public SummedPerlinNoise(uint _octavesNumber, double _persistence, AbstractNoiseGenerator _noiseGenerator, 
            AbstractInterpolator _interpolator, AbstractSmoother _smoother)
            : base(_noiseGenerator, _interpolator, _smoother)
        {
            this.octavesNumber = _octavesNumber;
            this.persistence = _persistence;
        }

        public override double perlinNoise1D(double x)
        {
            double result = 0.0d;
            double frequency;
            double amplitude;
            double weightSum = 0.0d;

            for (uint i = 0; i < this.octavesNumber; ++i) {
                frequency = Math.Pow(2, i);
                amplitude = Math.Pow(this.persistence, i);
                result += base.perlinNoise1D(x * frequency) * amplitude;
                weightSum += amplitude;
            }

            return result / weightSum;
        }

        public override double perlinNoise2D(double x, double y)
        {
            double result = 0.0d;
            double frequency;
            double amplitude;
            double weightSum = 0.0d;

            for (uint i = 0; i < this.octavesNumber; ++i) {
                frequency = Math.Pow(2, i);
                amplitude = Math.Pow(this.persistence, i);
                result += base.perlinNoise2D(x * frequency, y * frequency) * amplitude;
                weightSum += amplitude;
            }

            return result / weightSum;
        }

        public override double perlinNoise3D(double x, double y, double z) 
        {
            double result = 0.0d;
            double frequency;
            double amplitude;
            double weightSum = 0.0d;

            for (uint i = 0; i < this.octavesNumber; ++i) {
                frequency = Math.Pow(2, i);
                amplitude = Math.Pow(this.persistence, i);
                result += base.perlinNoise3D(x * frequency, y * frequency, z * frequency) * amplitude;
                weightSum += amplitude;
            }

            return result / weightSum;
        }

        public override double perlinNoise4D(double x, double y, double z, double t)
        {
            double result = 0.0d;
            double frequency;
            double amplitude;
            double weightSum = 0.0d;

            for (uint i = 0; i < this.octavesNumber; ++i) {
                frequency = Math.Pow(2, i);
                amplitude = Math.Pow(this.persistence, i);
                result += base.perlinNoise4D(x * frequency, y * frequency, z * frequency, t * frequency) * amplitude;
                weightSum += amplitude;
            }

            return result / weightSum;
        }
    }
}
